﻿using GenericWin;
using MessageBox;
using Services.OtherServices;
using GeneralServices;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace ScreenConnection
{
    /// <summary>
    /// Ecran de connexion du socle.
    /// Possède une liste de chammp que l'utilisateur aura paramétré dans son projet.
    /// Une méthode ShowScreen permet d'afficher la fenetre à l'écran.
    /// </summary>
    public class EcranConnection : Connection
    {
        #region Attribut(s)

        /// <summary>
        /// Liste des champs entrants
        /// </summary>
        private List<Champ> listChamp = new List<Champ>();

        public const String LDAPServer = @"LDAP://DCEPADDE03.constellium.biz/DC=constellium,DC=biz|LDAP://DCEPADDE01.constellium.biz/DC=constellium,DC=biz";
        public const String LDAPDomain = @"CONSTELLIUM";

        public static Int32 nbEtapeSplashScreenLongLoad = 3;
        public static Int32 nbEtapeSplashScreenShortLoad = 1;
        #endregion

        #region Propriété(s)

        /// <summary>
        /// Numéro de version de l'application
        /// </summary>
        public String Version
        {
            get;
            private set;
        }

        /// <summary>
        /// Nom de l'application
        /// </summary>
        public new String NomApp
        {
            get;
            private set;
        }

        /// <summary>
        /// Environnement de l'application
        /// </summary>
        public String Environnement
        {
            get;
            private set;
        }

        /// <summary>
        /// Savoir si l'autorisation est automatique ou non. Par défaut, Oui
        /// </summary>
        public Boolean Automatic
        {
            get;
            private set;
        }

        /// <summary>
        /// Controleur de l'application fille qui doit etre appelé pour gérer l'authentification, et appelé ensuite l'application générique
        /// </summary>
        public GenericMainController ConnectionView
        {
            get;
            private set;
        }

        /// <summary>
        /// Boolean qui est retourné lors de l'appel à la méthode ShowScreen permettant de savoir si l'authentification a réussi.
        /// </summary>
        public Boolean Auth
        {
            get;
            set;
        }

        /// <summary>
        /// Boolean qui est mis à true, si l'utilisateur a cliqué sur le bouton Annuler
        /// </summary>
        public Boolean Annuler
        {
            get;
            set;
        }

        /// <summary>
        /// Utilisateur connecté à l'écran de connection, null si l'authentification a échoué
        /// </summary>
        internal GENERAL_USER UserConnecte
        {
            get;
            set;
        }

        #region SplashScreen

        /// <summary>
        /// Permet d'afficher un splashscreen après que l'authentification est réussi pour pouvoir effectuer des modifications.
        /// Configuration du splash screen
        /// </summary>
        public static SplashScreenConfig SplashScreenConfig
        {
            get;
            private set;
        }
        
        #endregion


        #endregion

        #region Constructeur(s)

        /// <summary>
        /// Constructeur sans paramètres.
        /// Initialisation des paramètres par défaut.
        /// </summary>
        /// <param name="ctrl">Controller qui va gérer l'enchainement de l'application</param>
        /// <param name="splashScren">Définit si oui ou non, apres l'authentification, le splash scrren est appelé</param>
        public EcranConnection(GenericMainController ctrl, SplashScreenConfig splashScren = null)
            : this("1.0.0", "Inconnu", "Inconnu", ctrl, splashScren)
        {
        }

        /// <summary>
        /// Constructeur avec paramètres.
        /// </summary>
        /// <param name="version">Numéro de version</param>
        /// <param name="nomApp">Nom de l'application</param>
        /// <param name="env">Envrionnement de l'application</param>
        /// <param name="ctrl">Controller qui va gérer l'enchainement de l'application</param>
        /// <param name="splashScren">Définit si oui ou non, apres l'authentification, le splash scrren est appelé</param>
        public EcranConnection(String version, String nomApp, String env, GenericMainController ctrl, SplashScreenConfig splashScren = null)
            : this(version, nomApp, env, null, ctrl, splashScren)
        {
        }

        /// <summary>
        /// Constructeur avec paramètres.
        /// </summary>
        /// <param name="version">Numéro de version</param>
        /// <param name="nomApp">Nom de l'application</param>
        /// <param name="env">Envrionnement de l'application</param>
        /// <param name="list">Liste des champs. Par défaut, les champs Login et Password sont ajouté par défaut. Si il n'y pas d'autres champs à ajouter, mettre à null ce paramètre.</param>
        /// <param name="ctrl">Controller qui va gérer l'enchainement de l'application</param>
        /// <param name="splashScren">Définit si oui ou non, apres l'authentification, le splash scrren est appelé</param>
        public EcranConnection(String version, String nomApp, String env, List<Champ> list, GenericMainController ctrl, SplashScreenConfig splashScren = null)
        {
            Automatic = !ctrl.RedirectionAuthentification;
            if (SplashScreenConfig == null || (SplashScreenConfig != null && splashScren != null))
                SplashScreenConfig = splashScren;

            Version = version;
            NomApp = nomApp;
            Environnement = env;

            AddViewForCtrl(ctrl);
            Title = "Ecran de connexion " + NomApp;
            base.Closing += EcranConnection_Closing;

            listChamp.Add(new Champ("Utilisateur", TYPE.TEXT));
            listChamp.Add(new Champ("Mot de passe", TYPE.PASSWORD));
            listChamp = list != null ? listChamp.Concat(list).ToList() : listChamp;

            BuildView();
        }

        #endregion

        #region Méthode(s)

        #region Construction Ecran

        /// <summary>
        /// Va permettre de créer la vue en construisant les champs en dynamique
        /// </summary>
        private void BuildView()
        {
            // Met à jour les labels
            base.NomApp.Content = NomApp;
            VersionApp.Content = "Version " + Version;
            EnvApp.Content = "Connexion pour l'environnement de " + Environnement;

            /*** BUILD CHAMP ***/
            // Construit tous les champs dans la grille
            Grid g = GridChamp;
            g.VerticalAlignment = System.Windows.VerticalAlignment.Center;

            g.ColumnDefinitions.Add(new ColumnDefinition());
            g.ColumnDefinitions.Add(new ColumnDefinition());
            int row = 0;

            foreach (Champ champ in listChamp)
            {
                g.RowDefinitions.Add(new RowDefinition(){ Height = new GridLength(GridLength.Auto.Value + 40.0)});
                g.Children.Add(BuildText(champ, ref row));
                g.Children.Add(BuildChamp(champ, ref row));
                ++row;
            }
            /*** BUILD CHAMP ***/

            // On prend le focus sur le premier champ de l'écran de connection
            if (listChamp != null && listChamp.Count > 0)
                listChamp[0]._Champ.Focus();
        }
        
        #endregion

        #region Construction Element

        /// <summary>
        /// Construit un label devant un champ
        /// </summary>
        /// <param name="champ">Champ en question</param>
        /// <param name="row">Ligne sur laquelle ce champ va être ajouté</param>
        /// <returns>Retourne un Label</returns>
        private Label BuildText(Champ champ, ref int row)
        {
            Label label = new Label();

            label.Content = champ.Nom;
            label.Foreground = this.FindResource("ColorLabelConnection") as SolidColorBrush;
            label.FontWeight = (FontWeight)this.FindResource("FontWeightLabelConnection");

            label.HorizontalContentAlignment = System.Windows.HorizontalAlignment.Center;
            label.VerticalContentAlignment = System.Windows.VerticalAlignment.Center;

            Grid.SetColumn(label, 0);
            Grid.SetRow(label, row);

            return label;
        }

        /// <summary>
        /// Construit le champ entrant : TextBox, PasswordBox, ComboBox
        /// </summary>
        /// <param name="champ">Champ en question</param>
        /// <param name="row">Ligne sur laquelle ce champ va être ajouté</param>
        /// <returns>Retourne l'élément crée</returns>
        private UIElement BuildChamp(Champ champ, ref int row)
        {
            UIElement uie = BuildChamp(champ.Nom, champ.Type, champ.Values);
            champ._Champ = uie;
            Grid.SetColumn(uie, 1);
            Grid.SetRow(uie, row);
            return uie;
        }

        /// <summary>
        /// Méthode appelé par BuildChamp(champ, row) 
        /// </summary>
        /// <param name="nom">Nom du champ</param>
        /// <param name="type">Type du champ</param>
        /// <param name="values">liste de valeurs si combox</param>
        /// <returns>Retourne l'élément crée</returns>
        private UIElement BuildChamp(String nom, TYPE type, List<String> values)
        {
            UIElement t = null;

            switch (type)
            {
                case TYPE.TEXT:
                    t = new TextBox();
                    t.CommandBindings.Add(new System.Windows.Input.CommandBinding());
                    ((TextBox)t).Tag = nom;
                    break;

                case TYPE.PASSWORD:
                    t = new PasswordBox();
                    ((PasswordBox)t).Tag = nom;
                    break;

                case TYPE.LIST:
                    t = new ComboBox();
                    ((ComboBox)t).ItemsSource = values;
                    ((ComboBox)t).IsEditable = false;
                    break;

                default:
                    break;
            }

            ((Control)t).Tag = nom;
            ((Control)t).MaxHeight = 25;
            ((Control)t).MinHeight = 25;
            ((Control)t).Width = 150;
            ((Control)t).HorizontalContentAlignment = System.Windows.HorizontalAlignment.Center;
            ((Control)t).VerticalContentAlignment = System.Windows.VerticalAlignment.Center;

            return t;
        }
        
        #endregion

        #region Construction Dico

        /// <summary>
        /// Construit le dictionnaire qui permettra à l'utilisateur de checker les champs que l'utilisateur aura saisi.
        /// Chaque ligne du dictionnaire aura comme clé : Le nom du champ et la valeur sera soit la valeur du champ de texte ou l'item sélectionné pour une combo-box.
        /// Les clés pour les champs directements insérés seront : "Utilisateur" et "Mot de passe".
        /// </summary>
        /// <returns>Retourne un dictionnaire [Key, Value]</returns>
        public Dictionary<String, String> BuildDico()
        {
            Dictionary<String, String> dico = new Dictionary<string,string>();

            foreach(Champ champ in listChamp)
                dico.Add(champ.Nom, GetText(champ));

            return dico;
        }

        /// <summary>
        /// Obtient la valeur du champ pour celui passé en paramètre
        /// </summary>
        /// <param name="champ">champ de saisie</param>
        /// <returns>Retourne la valeur de ce champ</returns>
        private string GetText(Champ champ)
        {
            String value = String.Empty;

 	        switch(champ.Type)
            {
                case TYPE.TEXT:
                    value = ((TextBox)champ._Champ).Text;
                    break;

                case TYPE.PASSWORD:
                    value = ((PasswordBox)champ._Champ).Password;
                    break;

                case TYPE.LIST:
                    value = Convert.ToString(((ComboBox)champ._Champ).SelectedItem);
                    break;
            }

            return value;
        } 

        #endregion

        #region Ajout d'une fenetre qui observe

        /// <summary>
        /// Permet d'ajouter un controleur qui observe la fenetre de connexion.
        /// Ainsi le controleur doit implémenter l'interface IConnexionAuthentification.
        /// La méthode Authentificate sera appelé lorsque l'utilisateur cliquera sur le btn Valider.
        /// Cela permet à l'utilisateur d'implémenter sa propre authentification.
        /// Si l'authentification est OK, on appelle la méthode de construction de l'application générique du controleur
        /// </summary>
        /// <param name="connectionView">Controleur observable de la fenetre de connexion</param>
        public void AddViewForCtrl(GenericMainController ctrl)
        {
            ConnectionView = ctrl;
        }

        #endregion

        #region Evenement Fermeture Fenetre

        /// <summary>
        /// Cet événement peut etre appeler :
        /// Lors d'un clic sur le bouton Annuler : A ce moment, on ferme la fenetre sans réfléchir car Annuler est à true
        /// Lorsque l'utilisateur a cliquer sur le bouton Valider: : La deux choix :
        ///    -> Auth est à true : Donc tout c'est bien passé, on peut donc lancer la fenetre d'application générique avec le 
        ///             controleur principal qui a été initialisé avec celui passé en paramètre du constructeur de l'écran de connecion
        ///    -> Auth est à false : On affiche un message d'erreur et on arrete la fermeture 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private async void EcranConnection_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            if (!Annuler)
            {
                if (Auth)
                {
                    // Authentification OK
                    GenericApplication gen = ConnectionView.BuildGenericApplication(ConnectionView, UserConnecte, ConnectionView.BuildMenu());

                    if (SplashScreenConfig != null)
                    {
                        if (SplashScreenConfig.Content == null)
                            SplashScreenConfig.Content = new LibelleSplashScreen(gen.Config.NomApplication);

                        Splashscreen splash = new Splashscreen(SplashScreenConfig.Content);
                        splash.Show();

                        if (SplashScreenConfig.NbEtapeLongLoaded != null && SplashScreenConfig.NbEtapeLongLoaded > 0)
                            await ConnectionView.LongLoading(splash, SplashScreenConfig.NbEtapeLongLoaded, SplashScreenConfig.DelayActionsLong, SplashScreenConfig.NameActionsLong);

                        if (SplashScreenConfig.NbEtapeShortLoaded != null && SplashScreenConfig.NbEtapeShortLoaded > 0)
                            await ConnectionView.ShortLoading(splash, SplashScreenConfig.NbEtapeShortLoaded, SplashScreenConfig.DelayActionsShort, SplashScreenConfig.NameActionsShort);

                        splash.Reset();
                        splash.Close();
                        splash = null;
                    }

                    gen.Icon = this.Icon;
                    gen.WindowState = System.Windows.WindowState.Maximized;
                    gen.Show();
                }
                else
                {
                    // Message d'erreur
                    ShowMessageBox.Show(TYPE_WINDOW_BOX.ERREUR, "Utilisateur inconnu", String.Empty);
                    InitializeChamp();
                    e.Cancel = true;
                }
            }
        }

        private void InitializeChamp()
        {
            foreach (Champ champ in listChamp)
                champ.Initialize();
        }

        #endregion

        #endregion

    }
}
